#include "inc/my_include.h"
#include "inc/zcd_timer.h"
#include "inc/my_fifo.h"

inline int GetAdChCount(int adEn);
int ZT_CreateReadThread();
int ReadAndProcessAd();
void ExitExe( int i );

time_t beginTime; //用于看程序运行了多长时间
int firstRead = 0;
int adEn = 0x3f; //使能6个通道的AD
int rangeInx = 0; //±10V
#define groupFreq  10000//采集频率设为10K
int groupFreqDiv = 36000000 / groupFreq; //求出分频系数
int bc1s = 0; //1S的数据字节个数，在main函数中根据采集频率和通道号计算得出。
I16 zCode[AD_CH_COUNT]; //对零点原码值
I16 fCode[AD_CH_COUNT]; //对90%满度的原码值。比如±10V时，这是值就是9V对应的原码值
PSFIFO pSFifo;
int fd; //设备描述符

int main(int argc, char *argv[]) {
	fd = EM9118_Init();

	signal( SIGINT, ExitExe ); //接收ctrl-c信号，主要是调试用

	//分配32M缓冲区
	pSFifo = SFifoAlloc(32 * 1024 * 1024);
	if (pSFifo == NULL) {
		printf("SFifoAlloc Error");
	}
	firstRead = 1;
	//初始化内核缓冲区，内核缓冲区大小固定为819200
	int adChCount = GetAdChCount(adEn);
	EM9118_SFifoResize(fd, adChCount);
	//清空硬件FIFO
	EM9118_HFifoClear(fd);
	//设置通道使能
	EM9118_ADSetCh(fd, adEn);
	//设置采集范围
	EM9118_ADSetRangeAll(fd, rangeInx);
	//根据采集范围读出零点满度
	int i = 0;
	for (i = 0; i < AD_CH_COUNT; ++i)
		EM9118_RomWFWZRead(fd, i, rangeInx, &zCode[i], &fCode[i]);
	//设置采集频率
	EM9118_HCSetGroupFreq(fd, groupFreqDiv);
	//创建读数线程
	ZT_CreateReadThread();
	//启动采集
	EM9118_HCStart(fd, 1); //定时启动
	//使能中断
	EM9118BD_AdIrqEnable(fd, 1);
	U16 ctrl = EM9118_Inw( fd, ADDR_CTRLCODE );
	printf("ctrl:%x\n", ctrl);
	//计算每秒钟需要多少字节数
	bc1s = groupFreq * adChCount * sizeof(U16);
	while (1) {
		int ret = ReadAndProcessAd();
		if( ret < 0 )
			break;
	}

	EM9118_HCStop( fd );
	EM9118BD_AdIrqEnable(fd, 0);

	return 0;
}

//接收ctrl-c信号，主要是调试用
void ExitExe( int i )
{
	printf( "ctrl-c\n" );
	//必须得停止AD，否则内核缓冲区会溢出
	EM9118_HCStop( fd );
//	sleep(1);
	exit(0);
}

int ReadAndProcessAd()
{
	//等待数据量大于1S
	U32 i = 0;
	int cnt1s = bc1s / sizeof(U16);
	for (i = 0; i < 20; ++i) {
		U32 canReadNumber = SFifoCanReadNum(pSFifo);
		if (canReadNumber < cnt1s ) {
			usleep(100000);
		} else {
			break;
		}
//		printf( "ReadAndProcessAd:%d\n", canReadNumber );
	}
	if( i == 20 ) {
		U16 ctrl = EM9118_Inw( fd, ADDR_CTRLCODE );
		printf("not return in 2S!ctrl:%x\n", ctrl);
		return -1;
	}

	I16* adCode = malloc(bc1s);
	int ret = SFifoReadLock(pSFifo, adCode, cnt1s);
	if (ret == -ETIMEDOUT) {
		printf(" read time out!\n");
	} else if ( ret != cnt1s ){
		//没有读回足够数据，肯定是哪里出了问题，理论上不会出现这个情况
		printf( "ret:%d cnt1s:%d\n", ret, cnt1s );
		return -1;
	}
	F64 adSum[AD_CH_COUNT], adMax[AD_CH_COUNT], adMin[AD_CH_COUNT];
	for( i = 0; i < AD_CH_COUNT; ++i ) {
		adSum[i] = 0;
		adMax[i] = -1e9;//给一个很小的数，这样可以获得最大值
		adMin[i] = 1e9;//给一个很大的数，这样可以获得最小值
	}
	int chCount = GetAdChCount( adEn );
	for( i = 0; i < cnt1s; ++i ) {
		int chInx = i % chCount;
		adSum[chInx] += adCode[i];
		adMax[chInx] = max( adMax[chInx], adCode[i] );
		adMin[chInx] = min( adMin[chInx], adCode[i] );
	}
	//清屏
	printf( "\033[2J" ) ;
	printf( "channel\tvalue  \tdiff\n" );
	for( i = 0; i < chCount; ++i ) {
		//把源码值转换成物理值，此处是±10V输入
		F64 ev = adSum[i] / (cnt1s/chCount);
		F64 voltage = 10.0 * 0.9 * (ev - zCode[i]) / (fCode[i] - zCode[i]);
		F64 maxVoltage = 10.0 * 0.9 * (adMax[i] - zCode[i]) / (fCode[i] - zCode[i]);
		F64 minVoltage = 10.0 * 0.9 * (adMin[i] - zCode[i]) / (fCode[i] - zCode[i]);
		printf( "CH%d:  \t%7.4f\t%7.4f\n", i+1, voltage, maxVoltage - minVoltage );
	}
	free(adCode);
	return 0;
}

inline int GetAdChCount(int adEn) { //本函数得到所有进入fifo的通道数量，以16位整型为一个通道
	int adChCnt = 0;
	int i = 0;
	for (i = 0; i < AD_CH_COUNT; ++i) {
		if ((adEn >> i) & 1)
			++adChCnt;
	}
	return adChCnt;
}

void *ReadThread(void* p) {
	pthread_detach(pthread_self());

	//内核缓冲区大小是819200，每次从内核中读取数据量小于409600，内核缓冲区既不会溢出，也会有较高的效率
	//当采集速度比较低时，比如10KHz，每次读取数据比较少也不会对系统造成太大负担
	//每次读取10ms的数据量，注意不要让这个数为0
	int needCnt = GetAdChCount(adEn) * groupFreq / 100;
	needCnt = needCnt ? needCnt : 1;

	U16* pCode = malloc(needCnt * sizeof(U16));
	if (pCode == 0) {
		printf("ReadThread:malloc fails\n");
		return 0;
	}

	while (1) {
		U32 canReadNumber = EM9118_ADSFifoCanReadNumber(fd);
		if ( canReadNumber < needCnt) {
//			printf( "canReadNumber:%d\n", canReadNumber );
			continue;
		}

		int retCnt = EM9118_ADReadSFifo(fd, pCode, needCnt);
		if (retCnt < 0) {
			printf("EM9118_ADReadSFifo: fail: %d\n", retCnt);
			free(pCode);
			break;
		}

//		printf( "retCnt:%d\n", retCnt );

		//log_write( "data readed: %d", retCnt);
		if (SFifoCanWriteNum(pSFifo) < retCnt) {
			SFifoReadLock(pSFifo, 0, retCnt);
			pSFifo->overFlow++;
			printf("overFlow: %d\n", pSFifo->overFlow);
		}
		SFifoWriteLock(pSFifo, pCode, retCnt);
	}

	free(pCode);

	return 0;
}

int ZT_CreateReadThread() {
	pthread_t idSFifoRead;

	int ret = pthread_create(&idSFifoRead, NULL, (void*) ReadThread, NULL);
	if (ret) {
		printf("Create ReadThread error!\n");
		return -1;
	}
	return 0;
}
